package com.xwj.validation.unified.handler;

import com.xwj.common.util.R;
import com.xwj.common.util.enums.CommonErrorEnum;
import com.xwj.validation.common.utils.ErrorResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: xiaowajiang
 * @Date: 2022-03-14 20:42
 * @Description: 统一异常处理
 **/
@Slf4j
@RestControllerAdvice("com.xwj.validation.unified.controller") // 统一异常处理拦截的包
public class UnifiedExceptionHandler {

    /**
     * DataBinder 数据绑定访问器，集合校验中使用
     * @param dataBinder
     */
    @InitBinder
    private void activateDirectFieldAccess(DataBinder dataBinder) {
        dataBinder.initDirectFieldAccess();
    }

    /**
     * 参数校验(Valid)异常
     * 请求体为json数据，校验失败后，抛出 MethodArgumentNotValidException 异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public ResponseEntity handleValidException(MethodArgumentNotValidException e) {
        log.error("参数校验(Valid)异常，{}，异常类型：{}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        Map<String, String> errorMap = ErrorResultUtil.getErrorMap(bindingResult);
        CommonErrorEnum errorEnum = CommonErrorEnum.PARAM_VALID_ERROR;
        return ResponseEntity.status(errorEnum.getHttp())
                .body(R.error(errorEnum.getCode(), errorEnum.getMessage()).put("data", errorMap));
    }

    /**
     * 参数绑定异常
     * 请求体为form-data数据，校验失败后，抛出 BindException 异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = {BindException.class})
    public ResponseEntity handleValidException(BindException e) {
        log.error("参数绑定异常，{}，异常类型：{}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        Map<String, String> errorMap = ErrorResultUtil.getErrorMap(bindingResult);
        CommonErrorEnum errorEnum = CommonErrorEnum.PARAM_BIND_ERROR;
        return ResponseEntity.status(errorEnum.getHttp())
                .body(R.error(errorEnum.getCode(), errorEnum.getMessage()).put("data", errorMap));
    }

    /**
     * 约束校验异常
     * 使用 @RequestParam/@PathVariable 注解修饰的参数校验失败时，抛出 ConstraintViolationException 异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = {ConstraintViolationException.class})
    public ResponseEntity handleValidException(ConstraintViolationException e) {
        log.error("约束校验异常，{}，异常类型：{}", e.getMessage(), e.getClass());
        List<String> violations = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage).collect(Collectors.toList());
        String error = violations.get(0);
        CommonErrorEnum errorEnum = CommonErrorEnum.CONSTRAINT_VIOLATION_ERROR;
        return ResponseEntity.status(errorEnum.getHttp())
                .body(R.error(errorEnum.getCode(), errorEnum.getMessage()).put("data", error));
    }

    /**
     * 未知异常处理
     * @param t
     * @return
     */
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable t) {
        log.error("未知异常，{}，异常类型：{}", t.getMessage(), t.getClass());
        return R.error();

    }

}
